﻿using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace Wpf_Model_Centered
{
    /* 
     * 使用 Fody 提供的注入（code weaving）特性，可以使类自动实现属性变更通知接口，并在属性上触发事件
     *  参考 https://github.com/Fody/PropertyChanged
    */
    [PropertyChanged.AddINotifyPropertyChangedInterface]
    /*
     * 以下代码增加了多行注释并保留完整名空间以清晰讲解，实际使用 Heroius.Extension 库时更为精简。
     * 在阅读下面的代码前，浏览一下主窗体的设计页xaml代码（很简单）。
     */
    public class SampleEntity
    {
        /*
         * 使用 Hibernating 特性，显式地标记一个属性通过持久化方案加以保存。
         * Heroius.Files 库中的 EntitySilo 类提供了基于 xml 文件的自动保存/加载功能，其操作流程待结束本页余下内容后，回到主窗体继续。
         *  参考 https://heroius.com/store/help/files/html/5c38c95a-461c-4792-8f2d-a4e4fd8b314d.htm
         */
        [Heroius.Extension.Hibernating.Hibernating]
        public int IntProp { get; set; } = 1;

        /*
         * 使用 Presentation 特性，标记一个属性的界面显示配置。支持该特性的界面控件将自动创建界面元素，并在多数情况下支持双向数据绑定。
         * 特性中可以指定界面要素的：描述文本、提示文本、显示顺序、特殊类型等。
         *  参考 https://heroius.com/store/help/core/html/f2d500d9-8887-b112-161c-ec2806621181.htm
         */
        [Heroius.Extension.Presentation.Presentation(Label = "布尔值勾选项", Order = 1)]
        [Heroius.Extension.Hibernating.Hibernating]
        public bool BoolProp { get; set; } = true;

        /*
         * 在 WPF 程序中，使用 PropertyPanel 控件配合 Presentation 特性自动显示属性。
         * 若不指定特殊类型，则自动界面使用默认识别的元素显示。
         */
        [Heroius.Extension.Presentation.Presentation(Label = "字符串")]
        [Heroius.Extension.Hibernating.Hibernating]
        public string StrProp { get; set; } = "文本";

        /*
         * 在 Presentation 中指定类型后，PropertyPanel 会按照显示指定类型创建元素。
         * Heroius.Extension 还提供了部分类型的特殊支持。
         */
        [Heroius.Extension.Presentation.Presentation(Label = "文件", Type = Heroius.Extension.Presentation.PresentationSupportedType.File)]
        /*
         * 当指定属性为 File 类型时，可以通过 PresentationFile 特性补充描述。
         *  参考 https://heroius.com/store/help/core/html/8979e48f-7083-8831-f035-0434ca00660e.htm
         */
        [Heroius.Extension.Presentation.PresentationFile(Filter = "XML 文件|*.xml")]
        [Heroius.Extension.Hibernating.Hibernating]
        public string InputFileProp { get; set; } = "file.xml";

        /*
         * 指定展示类型为 MemoryCombo ，支持将字符串渲染到一个特殊的下拉框，MemoryComboBox。该下拉框可以记录曾经输入的内容，作为备选项。
         * MemoryComboBox 在 Heroius.Extension.WPF 库中定义。
         *  参考 https://heroius.com/store/help/wpf/html/dd996860-9af6-b363-5407-3593f5947173.htm
         */
        [Heroius.Extension.Presentation.Presentation(Label = "下拉选择", Type = Heroius.Extension.Presentation.PresentationSupportedType.MemoryCombo)]
        /*
         * 此时，可以通过 PresentationMemoryCombo 特性补充描述。
         *  参考 https://heroius.com/store/help/core/html/2b549d08-dfc0-81f9-a2ae-6b2ca6e126c2.htm
         */
        [Heroius.Extension.Presentation.PresentationMemoryCombo(IsEditable = true, StorageStringPropertyName = "HiddenOptions")]
        [Heroius.Extension.Hibernating.Hibernating]
        public string SelectProp { get; set; } = "value1";

        /*
         * 设置 Presentation 的 Order 为负值，此属性将不被界面展示。
         * 注意此属性的名称是上一属性的 PresentationMemoryCombo 特性所指定的 StorageStringPropertyName，
         *  即是说，下拉框的项从此属性的值创建。
         */
        [Heroius.Extension.Presentation.Presentation(Order = -1)]
        public string HiddenOptions { get; set; } = "value1\r\nvalue2\r\nvalue3";

        /*
         * PropertyPanel 支持字典类属性，字典被视为一种特殊的集合，界面元素为 CollectionManager 类型，在 Heroius.Extension.WPF 库中定义。
         *  参考 https://heroius.com/store/help/wpf/html/464d2baa-cba4-dc1c-8655-acb072cd8794.htm
         * CollectionManager 提供默认的集合管理界面，包括元素的创建、删除功能。对于能够采用默认值或无参数构造函数的元素类型，默认集合管理已经足够。
         */
        [Heroius.Extension.Presentation.Presentation(Label = "字典", Order = 2)]
        /*
         * 对于字典和集合属性，若要使用 EntitySilo 保存元素，则必须设置 EntitySilo 实例的 Recursion 和 HandleElements 为 true，
         *  且键、值、元素必须为简单类型或拥有无参数构造函数。
         * ※ 需要注意，若要使用 EntitySilo 加载元素，则集合类型必须实现 INotifyCollectionChanged 接口。
         * 由于字典类型不实现此接口，因此该属性无法通过 EntitySilo 加载，每次执行从文件加载时，字典会被清空。
         */
        [Heroius.Extension.Hibernating.Hibernating]
        public Dictionary<string, string> DictProp { get; set; } = new Dictionary<string, string>();

        /*
         * PropertyPanel 也支持集合类型。
         * 此属性特别地采用非值类型作为集合元素类型，以说明特殊集合支持。
         */
        [Heroius.Extension.Presentation.Presentation(Label = "复杂类型集合", Order = 2)]
        /*
         * 使用 PresentationCollection 特性对 Type = PresentationSupportedType.Collection 提供额外配置。
         * 通过指定创建、移除、清空的方法，覆盖默认的集合管理。
         * 当遇到元素类型没有无参数构造函数时，必须要指定单独定义的创建方法。
         * 该方法必须能在集合所在类型中找到，可以是实例方法、静态方法，也可以是外部代码定义的拓展方法。
         * 为使 CollectionManager 能够调用，创建方法必须符合 CollectionManager.CollectionManagerItemCreatingDelegate 委托。
         * 此例中，方法被定义在 SampleEntityExtension 类中。
         */
        [Heroius.Extension.Presentation.PresentationCollection(Operable = true, CreatingNewItemMethodName = "CreateNewItem")]
        /*
         * 若要使用 EntitySilo 加载元素，则集合类型必须实现 INotifyCollectionChanged 接口。
         * 该属性使用 ObservableCollection 泛型类，实现了接口，因此可以从文件加载。
         */
        [Heroius.Extension.Hibernating.Hibernating]
        public ObservableCollection<ComplexClass> CollectProp { get; set; } = new ObservableCollection<ComplexClass>();

        /*
         * 在默认情况下，PropertyPanel 不支持复杂类型的属性。
         * 但是可以通过指定其 Type 为 Special 创建特殊界面元素。
         */
        [Heroius.Extension.Presentation.Presentation(Type = Heroius.Extension.Presentation.PresentationSupportedType.Special, Order = 1)]
        /*
         * 使用 PresentationSpecial 特性对特殊属性进行处理，该属性的界面元素为可以点击的按钮。
         * 特性中指定处理按钮的文本，以及点击后执行的方法。
         *  参考 https://heroius.com/store/help/core/html/9f8bff90-caeb-aa4d-505c-f229282ab7bc.htm
         * 处理方法必须能在集合所在类型中找到，可以是实例方法、静态方法，也可以是外部代码定义的拓展方法。
         * 为使 PropertyPanel 能够调用，创建方法必须符合 PresentationSpecialAttribute.PresentationSpecialHandler 委托。
         * 此例中，方法被定义在 SampleEntity 类中，其不执行实际编辑，仅作为演示使用。
         */
        [Heroius.Extension.Presentation.PresentationSpecial(HandleLabel = "点击编辑", HandlerMethodName = "HandleComplexEdit")]
        [Heroius.Extension.Hibernating.Hibernating]
        public ComplexClass ComplexProp { get; set; } = new ComplexClass() { Name = "Old Property" };

        //用于演示复杂类型属性 ComplexProp 的处理响应
        public void HandleComplexEdit(object ui) { 
            IntProp++; // 双向绑定的实现得益于 Fody（见本页代码最上）
            MessageBox.Show("IntProp 已自加1"); 
        }

        /*
         * 更多的属性类型支持，请自行发掘。
         * 现在你可回到主窗体代码，阅读模型的保存与加载代码如何实现。
         */
    }

    //用于复杂类型演示
    [PropertyChanged.AddINotifyPropertyChangedInterface]
    public class ComplexClass
    {
        /*
         * 除 Presentation 特性外，也可以使用 Description 或者 Display 特性标记属性的显示名。
         */
        [Description("名字")]
        [Heroius.Extension.Hibernating.Hibernating]
        public string Name { get; set; } = "default name";
    }

    // 提供 SampleEntity 的拓展方法
    public static class SampleEntityExtension
    {
        //此方法用于响应 SampleEntity 类 CollectProp 属性的自动界面中的创建动作，由属性上的 PresentationCollection 特性指定。
        public static Heroius.Extension.WPF.CollectionManager.CollectionManagerItemCreateResult CreateNewItem(this SampleEntity entity, IList collection)
        {
            return new Heroius.Extension.WPF.CollectionManager.CollectionManagerItemCreateResult() { 
                NewItem = new ComplexClass() { Name = "new item" },
                Cancel = false
            };
        }
    }
}
